﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace myBoard.Framework.Package
{
    public static class PackageManager
    {
        public static void PackFileOnDisk(string inputFile, string destFolder)
        {
            if (!Directory.Exists(destFolder))
            {
                throw new DirectoryNotFoundException(string.Format("The specified directory for file extraction does not exist : {0}.", destFolder));
            }
            MemoryStream sr = PackFileInStream(inputFile);
            byte[] srArray = sr.ToArray();
            string extractionPath = Path.Combine(destFolder, Path.GetFileNameWithoutExtension(inputFile))+".mgt";
            using (FileStream fs = new FileStream(extractionPath, FileMode.Create, FileAccess.Write))
            {
                using (BinaryWriter writer = new BinaryWriter(fs))
                {
                    for (int i = 0; i < srArray.Length; i++)
                    {
                        writer.Write(srArray, i, 1);
                        writer.Flush();
                    }
                }
            }
        }

        public static void UnpackFile(string inputFile, string destFolder)
        {
            if (!Directory.Exists(destFolder))
            {
                throw new DirectoryNotFoundException(string.Format("The specified directory for file extraction does not exist : {0}.", destFolder));
            }

            using (MemoryStream ms = new MemoryStream())
            {
                int count = 0;
                byte[] buffer = new byte[0x800];
                using (FileStream fs = new FileStream(inputFile, FileMode.Open))
                {
                    using (BinaryWriter writer = new BinaryWriter(ms))
                    {
                        while ((count = fs.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            writer.Write(buffer, 0, count);
                            writer.Flush();
                        }
                    }
                }

                string lastThreeReadCaracters = string.Empty;
                string lastFourReadCaracters = string.Empty;
                byte[] srArray = ms.ToArray();
                byte[] suspectedHeader = new byte[4];
                bool fileHeaderStartDetected = false;
                bool fileHeaderEndDetected = false;
                bool shouldWriteBinaries = false;
                
                string fileName = string.Empty;
                string tempExtractedFileName = string.Empty;
                BinaryWriter binaryWriter = null;
                FileStream fileStream = null;

                for (int i = 0; i < srArray.Length; i++)
                {
                    if (fileHeaderEndDetected)
                    {
                        tempExtractedFileName = Path.GetTempFileName();
                        fileStream = new FileStream(tempExtractedFileName, FileMode.Create, FileAccess.Write);
                        binaryWriter = new BinaryWriter(fileStream);
                        shouldWriteBinaries = true;
                        fileHeaderEndDetected = false;
                    }
                    string s = lastFourReadCaracters + Encoding.ASCII.GetString(srArray, i, 1);
                    if (s.Contains(PackageConstants.PACKAGEDFILE_HEADER_START.Substring(0, 1)) ||
                        s.Contains(PackageConstants.PACKAGEDFILE_HEADER_START.Substring(0, 2)) ||
                        s.Contains(PackageConstants.PACKAGEDFILE_HEADER_START.Substring(0, 3)) ||
                        s.Contains(PackageConstants.PACKAGEDFOLDER_HEADER_START.Substring(0, 1)) ||
                        s.Contains(PackageConstants.PACKAGEDFOLDER_HEADER_START.Substring(0, 2)) ||
                        s.Contains(PackageConstants.PACKAGEDFOLDER_HEADER_START.Substring(0, 3)) ||
                        s.Contains(PackageConstants.PACKAGEDFOLDER_HEADER_START.Substring(0, 4)))
                    {
                        shouldWriteBinaries = false;
                    }
                    if (shouldWriteBinaries)
                    {
                        binaryWriter.Write(srArray, i, 1);
                        binaryWriter.Flush();
                    }
                    if (fileHeaderStartDetected)
                    {
                        if (fileStream != null)
                        {
                            string fullDestPath = Path.Combine(destFolder, fileName);
                            if (File.Exists(fullDestPath))
                                File.Delete(fullDestPath);
                            File.Copy(tempExtractedFileName, Path.Combine(destFolder, fileName));
                            fileStream.Close();
                            fileStream.Dispose();
                            binaryWriter.Close();
                            fileStream = null;
                            binaryWriter = null;
                            fileName = string.Empty;
                        }
                        fileName += Encoding.ASCII.GetString(srArray, i, 1);
                    }

                    lastThreeReadCaracters += Encoding.ASCII.GetString(srArray, i, 1);
                    lastFourReadCaracters += Encoding.ASCII.GetString(srArray, i, 1);

                    if (lastThreeReadCaracters == PackageConstants.PACKAGEDFILE_HEADER_START)
                    {
                        shouldWriteBinaries = false;
                        fileHeaderStartDetected = true;
                    }

                    if (lastThreeReadCaracters == PackageConstants.PACKAGEDFILE_HEADER_END)
                    {
                        fileHeaderEndDetected = true;
                        fileHeaderStartDetected = false;
                        fileName = fileName.Replace(PackageConstants.PACKAGEDFILE_HEADER_END, string.Empty);
                    }
                    if (lastThreeReadCaracters.Length == 3)
                    {
                        lastThreeReadCaracters = lastThreeReadCaracters.Remove(0, 1);
                    }
                    if (lastFourReadCaracters.Length == 4)
                    {
                        lastFourReadCaracters = lastFourReadCaracters.Remove(0, 1);
                    }
                }                
            }
        }

        public static void PackFolderOnDisk(string sourceFolder, string packedFolderPath)
        {
            if (!Directory.Exists(sourceFolder))
            {
                throw new DirectoryNotFoundException(string.Format("Specified folder does not exist {0}", sourceFolder));
            }
            DirectoryInfo di = new DirectoryInfo(sourceFolder);
            FileInfo[] files = di.GetFiles();
            using (FileStream fs = new FileStream(packedFolderPath, FileMode.Create))
            {
                using (BinaryWriter bw = new BinaryWriter(fs))
                {
                    foreach (FileInfo fi in files)
                    {
                        MemoryStream fileMemoryStream = PackFileInStream(fi.FullName);
                        byte[] buffer = fileMemoryStream.ToArray();
                        bw.Write(buffer, 0, buffer.Length);
                    }
                }
            }
        }

        public static void UnpackFolder(string folderToUnpack,string outputPath)
        {

        }

        public static MemoryStream PackFileInStream(string filePath)
        {
            if (!File.Exists(filePath))
                throw new FileNotFoundException(string.Format("Specified file does not exist {0}",filePath));
            
            MemoryStream ms = new MemoryStream();
            // First we encode the file header
            System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
            string header = string.Format("{0}{1}{2}", PackageConstants.PACKAGEDFILE_HEADER_START, Path.GetFileName(filePath), PackageConstants.PACKAGEDFILE_HEADER_END);
            byte[] encodedHeader = encoding.GetBytes(header);

            //Then we get the file binaries
            int count = 0;
            byte[] buffer = new byte[0x800];
            using (FileStream fs = new FileStream(filePath, FileMode.Open))
            {
                using (BinaryWriter writer = new BinaryWriter(ms))
                {
                    writer.Write(encodedHeader);
                    writer.Flush();
                    while ((count = fs.Read(buffer, 0, buffer.Length)) > 0)
                    {
                        writer.Write(buffer, 0, count);
                        writer.Flush();
                    }
                }
            }

            return ms;
        }

        public static Stream PackFolderInStream()
        {
            return null;
        }
    }
}
